package org.GenusApis;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.zip.GZIPInputStream;

import org.GenusApis.lib.GenusApisUtil;
import org.GenusApis.lib.HyvesApiException;
import org.GenusApis.lib.OAuthBase;
import org.GenusApis.lib.OAuthConsumer;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;

public class GenusApis {

    private final String DEFAULT_HA_FORMAT = "json";
    private final String DEFAULT_OAUTH_SIGNATURE_METHOD = "HMAC-SHA1";
    
    private OAuthConsumer OAuthConsumer;
    private String haVersion;

    public GenusApis(OAuthConsumer OAuthConsumer, String haVersion)
    {
        this.OAuthConsumer = OAuthConsumer;
        this.haVersion = haVersion;
    }
    
    public JSONObject doMethod(String haMethod, HashMap<String, String> providedParams) throws GeneralSecurityException, IOException, JSONException, HyvesApiException, URISyntaxException {
        return this.doMethod(haMethod, providedParams, null);
    }

    public JSONObject doMethod(String haMethod, HashMap<String, String> providedParams, OAuthBase OAuthToken) throws GeneralSecurityException, IOException, JSONException, HyvesApiException, URISyntaxException  {
        HashMap<String, String> params = new HashMap<String, String>();
        params.put("oauth_consumer_key", this.OAuthConsumer.getKey());
        params.put("oauth_timestamp", this.getOAuthTimestamp());
        params.put("oauth_nonce", this.getOAuthNonce());
        params.put("oauth_signature_method", this.DEFAULT_OAUTH_SIGNATURE_METHOD);
        params.put("ha_method", haMethod);
        params.put("ha_version", this.haVersion);
        params.put("ha_format", this.DEFAULT_HA_FORMAT);
        params.put("ha_fancylayout", "false");
        
        URI uri = new URI("http://data.hyves-api.nl/");
        
        String oauthTokenSecret = "";
        if (OAuthToken != null && OAuthToken.getKey() != null && OAuthToken.getSecret() != null) {
            params.put("oauth_token", OAuthToken.getKey());
            oauthTokenSecret = OAuthToken.getSecret();
        }
        params.putAll(providedParams);
        
        String signature = GenusApisUtil.calculateOAuthSignature("POST", uri.toURL().toString(), params, this.OAuthConsumer.getSecret(), oauthTokenSecret);
        params.put("oauth_signature", signature);
        
        List<BasicNameValuePair> nameValuePairs = new ArrayList<BasicNameValuePair>();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            nameValuePairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
        }
        
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(uri);
        httpPost.addHeader("Accept-Encoding", "gzip");
        httpPost.setEntity(OAuthUtil.getRFC2875EncodedStringEntity(nameValuePairs));
        HttpResponse response = httpClient.execute(httpPost);
        
        InputStream input = response.getEntity().getContent();
        Header contentEncoding = response.getFirstHeader("Content-Encoding");
        if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
            input = new GZIPInputStream(input);
        }
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        StringBuilder sb = new StringBuilder();
 
        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                input.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        JSONObject json = new JSONObject(sb.toString());
        this.checkForErrors(json);
        return json;
    }

    private void checkForErrors(JSONObject json) throws HyvesApiException, JSONException {
        if (json.has("error_code")) {
            throw new HyvesApiException(json.getString("error_message"), json.getInt("error_code"), json);
        }
    }
    
    private String getOAuthNonce() throws GeneralSecurityException {
        Random random = new Random();
        int nonce = random.nextInt();
        return String.valueOf(nonce);
    }

    private String getOAuthTimestamp() {
        return String.valueOf(System.currentTimeMillis()/1000);
    }
}
